Skip to main content

集合框架概述

集合和数组都是对多个数据进行存储操作的结构,简称Java容器。(此时的存储指内存层面)

Collection接口

​ 单列集合,用来存储一个一个的对象

​ 向Collection接口的实现类的对象中添加数据obj时,要求obj所在类要重写equals().

方法的使用:

      1.// add(Object e)--将元素e添加到集合coll中
coll.add("AA");
coll.add(123);
coll.add(new Date());

2.// size()--获取添加的元素的个数
System.out.println(coll.size());

3.// addAll()--将coll1中的元素全部分别添加到coll中
4.// 用add()添加会将另一个集合作为一个整体添加到该集合中
Collection coll1 = new ArrayList();
coll1.add("bb");
coll1.add(234);
coll.add(coll1);
System.out.println(coll);

5.// clear()--将coll集合清空,内部为null
coll.clear();

6.// isEmpty()--检查集合是否为空
System.out.println(coll.isEmpty());

7.// contains(Object obj)--判断当前集合中是否包含obj
// 类似String类中都经过了equals方法的重写,所以进行contains时对比的是new出来的内容,所以返回 ture,但自己写的类中若没有重写equals,仍是==判断,则会返回false。
coll.add(new String("Tom"));
System.out.println(coll.contains(new String("Tom")));

8.// containsAll(Collection coll1)--判断形参coll2中的所有元素是否都存在于当前集合中
Collection coll2 = Arrays.asList(123);
System.out.println(coll.containsAll(coll2));


9.// remove(Object obj)--从当前集合中移除obj元素
10.// removeAll(Collection coll1)--从当前集合中移除coll1中所有元素
Collection coll = new ArrayList();
coll.add(123);
coll.add(456);
coll.add(new String("jerry"));
coll.add(false);
coll.remove(123);
System.out.println(coll);
Collection coll1 = Arrays.asList(123, 456);
coll.removeAll(coll1);
System.out.println(coll);

11.// retainAll(Collection coll1)--交集,获取当前集合和coll1集合的交集,并返回给coll
Collection coll1 = Arrays.asList(123, 456, 789);
coll.retainAll(coll1);
System.out.println(coll);

12.// equals(Object obj)--判断当前集合和形参
13.// hashCode()--返回当前对象的哈希值
14.// 集合--->数组 toArray()
Object[] arr = coll.toArray();
for(int i = 0; i < arr.length; i++){
System.out.println(arr[i]);
}

15.// 集合--->数组 toArray()
Object[] arr = coll.toArray();
for(int i = 0; i < arr.length; i++){
System.out.println(arr[i]);
}

16.// 数组--->集合:调用Arrays类的静态方法asList()
List<String> list = Arrays.asList(new String[]{"AA", "BB", "CC"});
System.out.println(list);
List arr1 = Arrays.asList(123, 456);// new int[]{123, 456}会被认为是存了一个数据,new Integer[]{123, 456}则为2个shu
System.out.println(arr1);
-------------------------------遍历方法----------------------------------
//在Java中,使用增强for循环(也称为for-each循环)遍历一个集合(如ArrayList)并删除其中的元素会导致 ConcurrentModificationException异常。这是因为当你尝试在迭代过程中修改集合时,Java会检测到这种并发修改,然后抛出异常。

17.(1)// iterator()--迭代器iterator接口,返回iterator接口实例,用于遍历集合元素
Iterator iterator = coll.iterator();

// hasNext()判断是否还有下一个元素,!!!next()指针下移,并将下移后对应的元素返回!!!
while(iterator.hasNext()) {
System.out.println(iterator.next());
}

// remove()可在遍历时删除集合中的元素,不同于集合直接调用remove()
while (iterator.hasNext()){
Object obj = iterator.next();
if ("jerry".equals(obj)) {
iterator.remove();
}
}

(2)// foreach--增强for循环
//for (集合元素的类型 局部变量 : 集合(数组)对象)--将对象的值赋给局部变量
for (Object obj : coll) {
System.out.println(obj);
}

List接口:

存储有序的、可重复的数据 ---“动态”数组

ArrayList

作为List接口的主要实现类;线程不安全,效率高;底层使用Object[] 存储(如果确定存储大小,则创建时建议带上参数,提高效率)

		ArrayList list = new ArrayList();

list.add(123);
list.add(456);
list.add("AA");

1.// void add(int index, Object ele)--在index位置插入ele元素
list.add(1, "BB");

2.// boolean addAll(int index, Collection eles)--从index位置开始将eles中的所有元素添加到调用集合内
List list1 = Arrays.asList(1, 2, 3);
list.addAll(list1); // 结尾添加

3.// Object get(int index)--获取index位置的元素
System.out.println(list.get(1));

4.// int indexOf(Object obj)--返回obj在集合中首次出现的位置,如果不存在,返回-1
int index = list.indexOf(456);
System.out.println(index);

5.// int lastIndexOf(Object obj)--返回obj在集合中首次出现的位置,如果不存在,返回-1
System.out.println(list.lastIndexOf(456));

6.// Object remove(int index)--移除指定index位置的元素,并返回此元素
Object obj = list.remove(0);
System.out.println(obj);
System.out.println(list);

7.// Object set(int index, Object ele)--设置指定index位置的元素为ele
list.set(1, "CC");
System.out.println(list);

8.// List subList(int fromIndex, int toIndex)--返回从fromIndex到toIndex的左闭右开的值
List subList = list.subList(0, 2);
System.out.println(subList);
LinkedList

对于频繁的插入、删除操作,使用此类效率比ArrayList高;底层使用双向链表存储

Vector(略)

作为List接口的古老实现类;线程安全,效率低;底层使用Object[]存储

Set接口:

存储无序的、不可重复的数据 ---集合

要求向Set中添加的数据,其中所在的类一定要重写hashCode()和equals()

重写的hashCode()和equals()尽可能保持一致性

  1. 无序性:不等于随机性。存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值决定。

  2. 不可重复:保证添加的元素按照eqals()判断时,不能返回true。(在自定义的类中需要重写equals方法)。

HashSet

作为Set接口的主要实现类;线程不安全;可以存储null值

添加元素的过程(以HashSet为例):

​ 向HashSet中添加元素a,首先调用元素a所在类的hashCode()方法,计算元素a的哈希值,此哈希值接着通过某种算法计算出在HashSet底层数组中的存放 位置(即索引),判断数组此位置上是否有元素:

​ 如果没有,则a添加成功

​ 如果有其他元素b(或以链表形式存在的多个元素),则比较a与b的hash值:

​ 如果hash值不同,则元素a添加成功。

​ 如果hash值相同,需要调用a所在类的equals()方法:

​ equals()返回true,元素a添加失败

​ equals()返回false,元素a添加成功。

JDK7:元素a放到数组中,指向原来的元素。(类似栈)

JDK8:原来的元素在数组中,指向元素a。(类似链表)

在修改对应元素的值后

​ 相当于替换了原来位置的值,位置没变,但通过哈希值编码后的值变了,所以在进行remove操作时找到的位置大概率为空。

LinkedHashSet

作为HashSet的子类;遍历其内部数据时,可以按照添加的顺序遍历。在添加数据时会同时指向前一个数据和后一个数据。

优点:对于频繁的遍历操作,LinkedHashSet效率高于HashSet。

TreeSet

可以按照添加对象的指定属性进行排序。

  1. 向TreeSet中添加的数据,要求是相同类的对象

  2. 两种排序方式:自然排序(实现Comparable接口) 和 定制排序(Comparator)

    1. 自然排序中,比较两个对象是否相同的标准为:compareTo()返回0,不再是equals().

    2. 定制排序中,比较两个对象是否相同的标准为:compare()返回0,不再是equals().

      Comparator com = new Comparator() {
      // 按照年龄从小到大排列
      @Override
      public int compare(Object o1, Object o2) {
      if (o1 instanceof User && o2 instanceof User) {
      User u1 = (User)o1;
      User u2 = (User)o2;
      } else {
      throw new RuntimeException("输入的数据类型不匹配");
      }
      return 0;
      }
      };

Map接口(映射):双列集合,用来存储一对一对(key--value)的数据

key不能重复,无序(set)--->key所在的类要重写equals()和hashCode()(以HashMap为例)

value可重复,无序(collection)--->value所在的类要重写equals()(为了通过value找key)

一个键值对:key-value构成了一个Entry对象。

Map中的entry:无序的、不可重复的,使用Set存储所有的entry

HashMap:

作为Map的主要实现类;线程不安全的,效率高;存储null的key和value

HashMap的底层:数组+链表(jdk7及之前)

​ 数组+链表+红黑树(jdk8)

HashMap底层原理:

HashMap map = new HashMap();

在实例化后,底层创建了长度是16的一维数组Entry[] table。

……执行了多次put……

map.put(key1, value1):

首先,调用key1所在类的hashCode()计算key1哈希值,此哈希值经过某种算法计算后,得到在Entry数组中的存放位置。

如果此位置上的数据为空,此时的key1-value1添加成功。----情况1

如果此位置上的数据不为空,(意味着此位置上存在一个或多个数据(以链表形式存在)),比较key1和已经存在的一个或多个数据的哈希值:

注:哈希表的每个存储桶(或槽)可以包含一个链表,链表中的元素具有相同的哈希值。当新的键计算出与已有数据相同的哈希值时,新的键值对会被添加到该哈希值对应的链表中,而不会覆盖已存在的数据。

​ 如果key1的哈希值与已经存在的数据的哈希值都不相同,此时key1-value1添加成功。----情况2

​ 如果key1的哈希值与已经存在的某一个数据(key2-value2)的哈希值相同,继续比较:调用 key1所在类的equals(key2)方法:

​ 如果equals()返回false:此时key1-value1添加成功。----情况3

​ 如果equals()返回true:使用value1替换value2。

​ 补充:关于情况2和情况3:此时key1-value1和原来的数据以链表的方式存储。

​ 在不断的添加过程中,会涉及到扩容问题,默认的扩容方式:扩容为原来容量的2倍,并将原有的数据复制过来。

LinkedHashMap:

保证在遍历map元素时,可以按照添加的顺序实现遍历。

原因:在原有的HashMap底层结构基础上,添加了一对指针,指向前一个和后一个元素。对于频繁的遍历操作,此类执行效率高于HashMap。

CyrrentHashMap:

TreeMap:

保证按照添加的key-value对进行排序,实现排序遍历。此时考虑key的自然排序或定制排序。要求key使用同一个类进行创建

底层使用红黑树。

Hashtable:

作为古老的实现类;线程安全的,效率低;不能存储null的key和value

Properties:

常用来处理配置文件。key和value都是String类型

Collections工具类

一个操作Set,List,Map等集合的工具类

  • reverse(List):反转List中的元素

  • shuffle(List):对List集合元素进行随机排序

  • sort(List):根据元素的自然顺序对指定List集合元素按升序排序

  • sort(List, Comparator):根据指定的Comparator产生的顺序对List集合元素进行排序

  • swap(List, int, int):将指定List集合中的两个位置的元素进行交换

  • Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素

  • Object max(Collection, Comparator):根据Comparator指定的顺序,返回给定集合中的最大元素

  • Object min(Collection):

  • Object min(Collection, Comparator):

  • int frequency(Collection, Object):返回指定集合中指定元素的出现次数

  • void copy(List dest, List, src):将src中的内容复制到dest中

    赋值时:
    List dest = new ArrayList()
    Collections.copy(dest,list) // list已知,会报错,因为dest长度为0,小于list
    正确做法:
    List dest = Arrays.asList(new Object[list.size()])
    Collections.copy(dest, list)
    // 这时初始化了等长的NULL
  • boolean replaceAll(List list, Object oldVal, Object newVal):使用新值替换List对象中的旧值

  • List synchronizedList(list):将指定集合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全问题